package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.conf.*;
import server.conf.ServletWrapper;

import java.io.*;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 *
 * @author user
 */
public class Bootstrap {

    private final List<Service> services = new ArrayList<>();

    private static ThreadPoolExecutor threadPoolExecutor;

    public static ThreadPoolExecutor getThreadPoolExecutor() {
        return threadPoolExecutor;
    }

    /**
     * 初始化 server
     */
    private void initServer() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//Service");
            for (Element element : selectNodes) {
                Mapper mapper = new Mapper();
                // <servlet-name>lagou</servlet-name>
                Element connectorElement = (Element) element.selectSingleNode("Connector");
                String port = connectorElement.attributeValue("port");

                Service service = new Service(mapper);
                mapper.setPort(Integer.parseInt(port));

                List<Element> engine = element.selectNodes("Engine");
                Element engineElement = engine.get(0);
                List<Element> hosts = engineElement.selectNodes("Host");

                for (Element host : hosts) {
                    String name = host.attributeValue("name");
                    String appBase = host.attributeValue("appBase");

                    Host host1 = new Host();
                    host1.setHostUrl(host.attributeValue("hostUrl"));
                    host1.setAppBase(appBase);
                    host1.setName(name);
                    mapper.addHost(host1);
                }
                services.add(service);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化线程池
     */
    private void initThreadPoolExecutor() {
        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

    }

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {
        initThreadPoolExecutor();
        //
        initServer();


        for (Service service : services) {
            createService(service);
            // 加载解析相关的配置，web.xml
            loadServlet(service);
        }
    }

    /**
     * 创建 service
     *
     * @param service service
     * @throws IOException 异常
     */
    private void createService(Service service) throws IOException {
        Integer port = service.getMapper().getPort();
        ServerSocket serverSocket = new ServerSocket(port);
        SocketAccept socketAccept = new SocketAccept(service, serverSocket);
        System.out.println("=====>>>Minicat start on port：" + port);
        threadPoolExecutor.execute(socketAccept);
    }


    /**
     * 加载解析web.xml，初始化Servlet
     */
    private void loadServlet(Service service) throws FileNotFoundException {
        Mapper mapper = service.getMapper();
        List<Host> hosts = mapper.getHosts();
        if (hosts == null) {
            return;
        }
        for (Host host : hosts) {
            String appBase = host.getAppBase();
            System.out.println("load appBase:" + appBase);
            File appBaseDir = new File(appBase);
            File[] files = appBaseDir.listFiles();
            if (files == null) {
                System.err.println("load appBase error not find files:" + appBase);
                continue;
            }
            for (File file1 : files) {
                if (!file1.isDirectory()) {
                    continue;
                }
                Context context = new Context(file1);
                context.setUrl(file1.getName());
                File file = new File(file1, "web.xml");
                if (!file.isFile()) {
                    System.err.println("错误配置文件 web.xml ：" + file.getAbsolutePath());
                    return;
                }

                System.out.println("load appBase host:" + appBase + "  " + context.getUrl());
                Map<String, ServletWrapper> servletMap = this.loadServletXml(file, context);
                context.setServletMap(servletMap);
                host.addContext(context);
            }
        }

    }

    /**
     * 解析 web.xml 内容
     *
     * @param file    文件
     * @param context context
     * @return map
     * @throws FileNotFoundException file not found
     */
    private Map<String, ServletWrapper> loadServletXml(File file, Context context) throws FileNotFoundException {
        InputStream resourceAsStream = new FileInputStream(file);
        SAXReader saxReader = new SAXReader();
        Map<String, ServletWrapper> servletMap = new HashMap<>();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element element : selectNodes) {
                // <servlet-name>lagou</servlet-name>
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletnameElement.getStringValue();
                // <servlet-class>LagouServlet</servlet-class>
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletclassElement.getStringValue();


                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                // /lagou
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                ClassLoader classLoader = context.getClassLoader();

                ServletWrapper servlet = new ServletWrapper();
                servlet.loadClassName(servletClass, classLoader);
                servlet.setName(servletName);
                servlet.setUrl(urlPattern);
                servletMap.put(urlPattern, servlet);
            }
        } catch (DocumentException | IllegalAccessException | InstantiationException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return servletMap;
    }


    /**
     * Minicat 的程序启动入口
     *
     * @param args 参数
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
